macro_rules! def_enum {
    (
        $(#[$meta:meta])*
        $vis:vis enum $name:ident($repr:ident) {
             $($variant:ident = $val:literal $(| $alt:literal)? ),* $(,)?
        }
    ) => {

        $(#[$meta])*
        #[repr($repr)]
        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
        $vis enum $name { $($variant = $val),* }

        #[allow(unused)]
        $vis use $name::*;

        impl $name {
            $vis const fn repr(self) -> $repr {
                match self {
                    $( $name::$variant => $val, )*
                }
            }
            $vis const fn from_repr(repr: $repr) -> ::core::option::Option<Self> {
                match repr {
                    $( $val $(| $alt)? => ::core::option::Option::Some($name::$variant), )*
                    _ => None,
                }
            }
            $vis const unsafe fn from_repr_unchecked(repr: $repr) -> Self {
                match repr {
                    $( $val $(| $alt)? => $name::$variant, )*
                    _ => ::core::hint::unreachable_unchecked(),
                }
            }
        }

        impl ::core::convert::TryFrom<$repr> for $name {
            type Error = $crate::Invalid<$name>;

            fn try_from(repr: $repr) -> ::core::result::Result<Self, Self::Error> {
                Self::from_repr(repr).ok_or($crate::Invalid(::core::marker::PhantomData))
            }
        }

        impl ::core::convert::From<$name> for $repr {
            fn from(this: $name) -> $repr {
                this.repr()
            }
        }
    };
}

macro_rules! impl_add_sub_modulo {
    ($ty:ty, $modulo:literal, $(($unsigned:ty, $signed:ty)),* $(,)?) => {
        $(
        impl ::core::ops::Add<$unsigned> for $ty {
            type Output = $ty;

            fn add(self, rhs: $unsigned) -> $ty {
                let result = (self.repr() as $unsigned + rhs) % $modulo;
                unsafe { <$ty>::from_repr_unchecked(result as _) }
            }
        }

        impl ::core::ops::Add<$signed> for $ty {
            type Output = $ty;

            fn add(self, rhs: $signed) -> $ty {
                let result = ((self.repr() as $signed + rhs) % $modulo + $modulo) % $modulo;
                unsafe { <$ty>::from_repr_unchecked(result as _) }
            }
        }

        impl ::core::ops::Sub<$signed> for $ty {
            type Output = $ty;

            fn sub(self, rhs: $signed) -> $ty {
                self + (-rhs)
            }
        }
        )*

        impl<T> ::core::ops::AddAssign<T> for $ty
        where
            $ty: ::core::ops::Add<T, Output = $ty>,
        {
            fn add_assign(&mut self, rhs: T) {
                *self = *self + rhs;
            }
        }

        impl<T> ::core::ops::SubAssign<T> for $ty
        where
            $ty: ::core::ops::Sub<T, Output = $ty>,
        {
            fn sub_assign(&mut self, rhs: T) {
                *self = *self - rhs;
            }
        }
    };
}

macro_rules! impl_display {
    ($ty:ty = Debug) => {
        impl_display!($ty = |self, f| { ::core::fmt::Debug::fmt(self, f) });
    };
    ($ty:ty = |$self:ident| Display($expr:expr)) => {
        impl_display!($ty = |$self, f| { $expr.fmt(f) });
    };
    ($ty:ty = |$self:ident, $f:ident| $expr:expr) => {
        impl ::core::fmt::Display for $ty {
            fn fmt(&$self, $f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                $expr
            }
        }
    };
}

macro_rules! impl_debug {
    ($ty:ty = |$self:ident, $f:ident| $expr:expr) => {
        impl ::core::fmt::Debug for $ty {
            fn fmt(&$self, $f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                $expr
            }
        }
    };
}
